#include "Character.h"
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include "GFuns.h"
#include "Scaler.h"
#include "NameGenerator.h"

Character::Character()
{
}

Character::Character(int aJob, int aBiome, bool aHero)
{
	// generate parameters
	male = rand()%2;
	seed = rand();
	mood = (EMood)(rand()%MOOD_END);
	int minHeight = 100;
	int maxHeight = 200;
	int minWeight = 40;
	int maxWeight = 120;
	job = (EJob)(aJob);
	age = (EAge)(rand()%AGE_END);
	speed = randInt(3,10);
	gold = randInt(500,1000);

	robbed = false;
	isGhost = false;

	mobile = false;
	leftFoot = true;
	traces.clear();

	// generate race accroding to a biome
	race = RACE_WHITE;
	if(aBiome == SAVANNA || aBiome == DESERT || aBiome == STEPPE) race = RACE_BLACK;
	if(aBiome == TAIGA || aBiome == TUNDRA || aBiome == POLAR) race = RACE_NORD;
	if(rand()%15 == 0)	race = (ERace)(rand()%RACE_END);

	// change age according to a job
	if(job != JOB_UNEMPLOYED && age == AGE_CHILD) age = AGE_MATURE;
	if(job == JOB_SOLDIER) age = rand()%2 ? AGE_MATURE : AGE_TEEN;
	if(job == JOB_KING)	age = rand()%2 ? AGE_MATURE : AGE_OLD;

	// change parameters according to an age
	if(age == AGE_CHILD)
	{
		maxHeight = 120;
		maxWeight = 50;
		speed = randInt(3,5);
	}
	if(age == AGE_OLD)
	{
		maxHeight = 140;
		minWeight = 70;
		maxWeight = 90;
		speed = randInt(8,10);
	}

	// change parameters according to a job
	if(job == JOB_KING)
	{
		minWeight = 80;
		male = true;
	}
	if(job == JOB_SOLDIER)
	{
		minHeight = 180;
		minWeight = 100;
		male = true;
	}
	if(job == JOB_PRIEST)
	{
		minHeight = 150;
		minWeight = 80;
		male = true;
	}
	if(job == JOB_WORKER)
	{
		male = true;
	}
	if(job == JOB_ARMORSMITH || job == JOB_WEAPONSMITH)
	{
		minWeight = 80;
		male = true;
	}
	if(job == JOB_BARTENDER)
	{
		minWeight = 100;
		male = true;
	}
	if(job == JOB_CHEF)
	{
		minWeight = 100;
	}
	if(job == JOB_HERBALIST)
	{
		minHeight = 140;
		minWeight = 70;
		maxWeight = 90;
		male = false;
	}

	// change parameters according to sex
	if(!male)
	{
		maxHeight = 180;
	}

	// apply size parameters
	height = randInt(minHeight,maxHeight);
	weight = randInt(minWeight,maxWeight);

	// hair color
	if(age == AGE_OLD) hairColor = HAIR_WHITE;
	else if(job == JOB_PRIEST) hairColor = HAIR_BROWN;
	else
	{
		if(race == RACE_BLACK)
		{
			if(rand()%2 == 0)  hairColor = HAIR_BLACK;
			else  hairColor = HAIR_BROWN;
		}
		else if(race == RACE_NORD)
		{
			if(rand()%2 == 0)  hairColor = HAIR_BLONDE;
			else  hairColor = HAIR_RED;
		}
		else hairColor = rand()%(HAIR_WHITE);
	}

	// hair style
	if(male)
	{
		hairCut = rand()%(HAIRF_END);
		if(age == AGE_OLD && hairCut == HAIR_MOHAWK) hairCut = HAIR_BALD;
		if(age == AGE_CHILD && (hairCut == HAIR_BALD || hairCut == HAIR_MOHAWK)) hairCut = HAIR_SHORT;
		if(job == JOB_PRIEST) hairCut = HAIR_BALD;
		if(job == JOB_KING) hairCut = HAIR_SHORT;
		if(job == JOB_SOLDIER && hairCut == HAIR_SHORT) hairCut = HAIR_UNDERCUT;
		if(job == JOB_CHEF && hairCut == HAIR_MOHAWK) hairCut = HAIR_SHORT;
	}
	else
	{
		hairCut = rand()%(HAIRM_END);
		if(age == AGE_OLD && (hairCut == HAIR_ODANGO || hairCut == HAIR_HIME)) hairCut = HAIR_PIGTAILS;
	}

	if(aHero) speed = 1;

	name = NameGenerator::GenerateName(race, male);
	if(job == JOB_KING)
	{
		name = "King " + name;
	}

	inventory = new Inventory((EJob)job, male);

	intoxication = 0;

	// Creating a skeleton
	Scaler h(100,200,0,11);
	Scaler w(40,120,0,9);
	skeleton = new Skeleton();
	skeleton->Build(h.scale(height),w.scale(weight),male,speed);

	// character independent
	mapPositionX = 0;
	mapPositionY = 0;
	facing = rand()%4;
	if(facing == UP) facing = DOWN;
	defaultFacing = facing;
	hasChanged = true;
	animationFrame = 0;
	animation = ANIMATION_NONE;
	isMoving = false;
	stopFlag = false;
}

Character::~Character()
{
	delete inventory;
	delete skeleton;
}

void Character::RegenerateCharacter()
{
	// generate parameters
	male = rand()%2;
	seed = rand();
	int minHeight = 100;
	int maxHeight = 200;
	int minWeight = 40;
	int maxWeight = 120;
	job = JOB_UNEMPLOYED;
	age = (EAge)(rand()%AGE_END);
	speed = 1;
	gold = randInt(500,1000);

	race = (ERace)(rand()%RACE_END);

	// change parameters according to an age
	if(age == AGE_CHILD)
	{
		maxHeight = 120;
		maxWeight = 50;
	}
	if(age == AGE_OLD)
	{
		maxHeight = 140;
		minWeight = 70;
		maxWeight = 90;
	}

	// change parameters according to sex
	if(!male)
	{
		maxHeight = 180;
	}

	// apply size parameters
	height = randInt(minHeight,maxHeight);
	weight = randInt(minWeight,maxWeight);

	// hair color
	if(age == AGE_OLD) hairColor = HAIR_WHITE;
	else
	{
		if(race == RACE_BLACK)
		{
			if(rand()%2 == 0)  hairColor = HAIR_BLACK;
			else  hairColor = HAIR_BROWN;
		}
		else if(race == RACE_NORD)
		{
			if(rand()%2 == 0)  hairColor = HAIR_BLONDE;
			else  hairColor = HAIR_RED;
		}
		else hairColor = rand()%(HAIR_WHITE);
	}

	// hair style
	if(male)
	{
		hairCut = rand()%(HAIRF_END);
		if(age == AGE_OLD && hairCut == HAIR_MOHAWK) hairCut = HAIR_BALD;
		if(age == AGE_CHILD && (hairCut == HAIR_BALD || hairCut == HAIR_MOHAWK)) hairCut = HAIR_SHORT;
		if(job == JOB_PRIEST) hairCut = HAIR_BALD;
		if(job == JOB_KING) hairCut = HAIR_SHORT;
		if(job == JOB_SOLDIER && hairCut == HAIR_SHORT) hairCut = HAIR_UNDERCUT;
		if(job == JOB_CHEF && hairCut == HAIR_MOHAWK) hairCut = HAIR_SHORT;
	}
	else
	{
		hairCut = rand()%(HAIRM_END);
		if(age == AGE_OLD && (hairCut == HAIR_ODANGO || hairCut == HAIR_HIME)) hairCut = HAIR_PIGTAILS;
	}

	name = NameGenerator::GenerateName(race, male);

	// Inventory
	delete inventory;
	inventory = new Inventory((EJob)job, male);

	// Status
	intoxication = 0;

	// Creating a skeleton
	delete skeleton;
	Scaler h(100,200,0,11);
	Scaler w(40,120,0,9);
	skeleton = new Skeleton();
	skeleton->Build(h.scale(height),w.scale(weight),male,speed);

	// character independent
	hasChanged = true;
}

bool Character::HaveCloth(ECloth aType)
{
	for(unsigned int i=0; i<inventory->clothes.size(); i++)
	{
		Item* cloth = &(inventory->clothes[i]);
		if(cloth->GetItemSubType() == aType && cloth->IsEquipped()) return true;
	}

	return false;
}

bool Character::HaveItem(Item* aItem)
{
	if(aItem->GetItemType() == ITEM_CLOTH)
	{
		for(unsigned int i=0; i<inventory->clothes.size(); i++)
		{
			Item* cloth = &(inventory->clothes[i]);
			if(cloth->GetItemSubType() == aItem->GetItemSubType()) return true;
		}
	}

	for(unsigned int p=0; p<inventory->self.size(); p++)
	{
		for(unsigned int i=0; i<inventory->self[p].items.size(); i++)
		{
			Item* item = &(inventory->self[p].items[i]);
			if((item->GetItemType() == aItem->GetItemType()) && (item->GetItemSubType() == aItem->GetItemSubType()))
				return true;
		}
	}

	return false;
}

bool Character::HaveDress()
{
	for(unsigned int i=0; i<inventory->clothes.size(); i++)
	{
		Item* cloth = &(inventory->clothes[i]);
		if(!cloth->IsEquipped()) continue;
		ECloth clothType = (ECloth)cloth->GetItemSubType();
		if(clothType == CLOTH_APRON_HERB || clothType == CLOTH_DRESS 
		  || clothType == CLOTH_PETTICOAT || clothType == CLOTH_SKIRT)
				return true;
	}

	return false;
}

void Character::SetPosition(int aX, int aY)
{
	mapPositionX = aX;
	mapPositionY = aY;
	positionOffsetX = 0;
	positionOffsetY = 0;

	hasChanged = true;
}

void Character::SetFacing(int aX, int aY)
{
	int dir = 0;
	Node v = {aX-GetPositionX(), aY-GetPositionY()};

	if(abs(v.x)+abs(v.y) < 5)
	{
		if(v.x > 0)
		{
			dir = RIGHT;
			if(-v.y > v.x) dir = UP;
			if(v.y > v.x) dir = DOWN; 
		}
		else
		{
			dir = LEFT;
			if(-v.y > -v.x) dir = UP;
			if(v.y > -v.x) dir = DOWN; 
		}

		SetFacing(dir);
	}
	else
		SetFacing(defaultFacing);
}

void Character::Move(int aDir)
{
	int anim;
	switch(aDir)
	{
	case LEFT:  anim = ANIMATION_WALK_LEFT; break;
	case RIGHT: anim = ANIMATION_WALK_RIGHT; break;
	case UP:    anim = ANIMATION_WALK_UP; break;
	case DOWN:  anim = ANIMATION_WALK_DOWN; break;
	}

	if(animation != anim) skeleton->StartAnimation(anim);
	animation = anim;
	facing = aDir;

	isMoving = true;
	positionOffsetX = 0;
	positionOffsetY = 0;
}

void Character::Stop()
{
	if(!stopFlag) stopFlag = true;

	if(!isMoving)
	{
		skeleton->StopAnimation();
		animation = ANIMATION_NONE;
		positionOffsetX = 0;
		positionOffsetY = 0;
		//facing = DOWN;
		stopFlag = false;
	}
}

void Character::Tick()
{
	animationFrame++;

	if(intoxication > 0.0)
	{
		intoxication -= 0.0002;
	}

	if(isMoving)
	{
		float minStep = 0.05/speed;
		if(animation == ANIMATION_WALK_RIGHT)
		{
			if(positionOffsetX >= 0)
			{
				mapPositionX++;
				positionOffsetX = -1;
			}

			positionOffsetX+=minStep;

			if(positionOffsetX >= 0)
			{
				isMoving = false;
			}
		}

		if(animation == ANIMATION_WALK_LEFT)
		{
			if(positionOffsetX <= 0)
			{
				mapPositionX--;
				positionOffsetX = 1;
			}

			positionOffsetX-=minStep;

			if(positionOffsetX <= 0)
			{
				isMoving = false;
			}
		}

		if(animation == ANIMATION_WALK_UP)
		{
			if(positionOffsetY <= 0)
			{
				mapPositionY--;
				positionOffsetY = 1;
			}

			positionOffsetY-=minStep;

			if(positionOffsetY <= 0)
			{
				isMoving = false;
			}
		}

		if(animation == ANIMATION_WALK_DOWN)
		{
			if(positionOffsetY >= 0)
			{
				mapPositionY++;
				positionOffsetY = -1;
			}

			positionOffsetY+=minStep;

			if(positionOffsetY >= 0)
			{
				isMoving = false;
			}
		}

		if(traceOn)
		{
			static int steps = 4;
			static float babyStep = 1.0/steps;
			for(int i=0; i<steps; i++)
			{
				if(((abs(abs(positionOffsetX) - babyStep*i) < minStep/2) && positionOffsetX != 0)
				|| ((abs(abs(positionOffsetY) - babyStep*i) < minStep/2) && positionOffsetY != 0))
				{
					Point pos = {mapPositionX + positionOffsetX, mapPositionY + positionOffsetY};

					if(animation == ANIMATION_WALK_DOWN || animation == ANIMATION_WALK_UP)
					{
						if(leftFoot) pos.x += 0.05;
						else pos.x -= 0.05;
					}
					else
					{
						if(leftFoot) pos.y += 0.025;
						else pos.y -= 0.025;
					}
					if(leftFoot) leftFoot = false;
					else leftFoot = true;

					Trace tr = {pos,0.25,true};
					traces.push_back(tr);
					break;
				}
			}
		}
	}
	if(stopFlag) Stop();

	if(traces.size() > 0)
	{
		for(list<Trace>::iterator i=traces.begin(); i!=traces.end(); ++i)
		{
			i->alpha -= 0.001;
			if(i->alpha <= 0) 
			{
				i = traces.erase(i);
				if(i == traces.end()) break;
			}
		}
	}

	skeleton->Tick();

	hasChanged = true;
}